//TODO: Test, ZeroCrossingEdge
//This filter is tested and has got some issues

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// ZeroCrossing is an algorithm to find the zero crossings in an image
    /// </summary>
    public class ZeroCrossingEdge : BaseForeBackgroundColorFilter
    {
        //The Convolution operator used as part of the process.
        //Convolution convolution;

        //The Gaussian Smoothing operator used as part of the process
        //    GaussianSmooth gaussiansmooth;
        //the width and height of the output image
        //The possibls kernels to be used in the Laplacian function

        /// <summary>
        /// 
        /// </summary>
        protected double[,] laplacian1 = { { 0, 1, 0 }, { 1, -4, 1 }, { 0, 1, 0 } };
        /// <summary>
        /// 
        /// </summary>
        protected double[,] laplacian2 = { { 1, 1, 1 }, { 1, -8, 1 }, { 1, 1, 1 } };
        /// <summary>
        /// 
        /// </summary>
        protected double[,] laplacian3 = { { -1, 2, -1 }, { 2, -4, 2 }, { -1, 2, -1 } };

        /// <summary>
        /// 
        /// </summary>
        protected float m_fTheta = 8.0f;
        /// <summary>
        /// 
        /// </summary>
        protected bool m_bLimiter = true;

        /// <summary>
        /// 
        /// </summary>
        protected float m_fLimit = 0.4f;

        protected GaussianSmooth m_Gaussian = new GaussianSmooth();
        protected OwnOptimizedConvolutionDouble m_OwnOptimizedConvolutionDouble = new OwnOptimizedConvolutionDouble();

        /// <summary>
        /// default constructor
        /// </summary>
        public ZeroCrossingEdge()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ZeroCrossingEdge"/> class.
        /// </summary>
        /// <param name="_zeroCrossingEdge">The _zero crossing edge.</param>
        internal ZeroCrossingEdge(ZeroCrossingEdge _zeroCrossingEdge)
        {
        }

        /// <summary>
        /// Gets or sets the theta.
        /// </summary>
        /// <value>The theta.</value>
        public float Theta
        {
            get { return m_fTheta; }
            set { m_fTheta = value; }
        }

        /// <summary>
        /// executes this filter
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            ApplyZeroCrossing(_bitmap, 0, m_fTheta, m_bLimiter, m_fLimit);
            return _bitmap;
        }

        /// <summary>
        /// a) assume the image is grey level (hence RR=GG=BB)
        /// b) use value 0x000000ff to get the BB value
        /// c) Apply gaussian smoothng to the image
        /// d) Convolve the laplacian kernel with the image
        /// e) Find the points where the answer switches from +ve to -ve
        ///  f) If required limit these points using the supplied thereshold
        /// g) Return the zero crossing image         
        /// </summary>
        /// <param name="_bitmap">The source image as a pixel array</param>
        /// <param name="kersize">kersize The size of kernel to be applied</param>
        /// <param name="theta">theta The standard deviation of the gaussian smoothing</param>
        /// <param name="bLimiter">limiter specifies whether to limit the zero crossing</param>
        /// <param name="limit">limit The limit applied to the zero crossings</param>
        protected void ApplyZeroCrossing(UnsafeBitmap _bitmap,
                         int kersize, float theta, bool bLimiter,
                         float limit)
        {
            double dThreshold = 0.0f;

            //Set the threshold value
            if (bLimiter)
            {
                dThreshold = limit;
            }
            else
            {
                dThreshold = 0;
            }

            //Gaussian Smooth the initial image

            //TODO
            m_Gaussian.Width = (int)theta;
            m_Gaussian.Execute(_bitmap);
            m_OwnOptimizedConvolutionDouble.Kernel = laplacian1;
            m_OwnOptimizedConvolutionDouble.Execute(_bitmap);
            
            //Now need to find points in the array which are zero crossings  
            
            // To find the zero crossings in the image you must check each point 
            // in the array to see if it lies on a zero crossing. This is done by
            // checking the neighbours around the pixel.

            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;

            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    if (x - 1 < 0 || y - 1 < 0 || x + 1 > nWidth - 1 || y + 1 > nHeight - 1)
                    {
                        continue;
                    }
                    ApplyNeighbours(_bitmap, x, y, _bitmap, dThreshold);
                }
            }
        }

        /// <summary>
        /// check_neighbours is used to check the neighbourhood of a
        /// pixel to see if there is a zero crossing at the pixel
        /// currently being considered. This is done by seeing if there
        /// is a change in sign between the two opposite pixels on either
        /// side of the middle pixel. This is done in each of the four
        /// relevant directions. If there is a change the point is set to
        /// white and if there is no change it is set to black
        /// </summary>
        /// <param name="tmp_2d">The TMP_2D.</param>
        /// <param name="x">The x.</param>
        /// <param name="y">The y.</param>
        /// <param name="bitmap">The bitmap.</param>
        /// <param name="dThreshold">The d threshold.</param>
        private void ApplyNeighbours(UnsafeBitmap tmp_2d, int x, int y, UnsafeBitmap bitmap, double dThreshold)
        {
            /*If when neighbouring points are multiplied the result is -ve
             *then there must be a change in sign between these two points.
             *If the change is also above the thereshold then set it as a 
             *zero crossing.
             */
            if (tmp_2d[x - 1, y].ToArgb() * tmp_2d[x + 1, y].ToArgb() < 0)
            {
                if (Math.Abs(tmp_2d[x - 1, y].ToArgb()) + Math.Abs(tmp_2d[x + 1, y].ToArgb()) > dThreshold)
                {
                    bitmap.SetPixel(x - 1, y - 1, m_ColorForeground);
                }
                else
                {
                    bitmap.SetPixel(x - 1, y - 1, m_ColorBackground);
                }
            }

            else if (tmp_2d[x - 1, y - 1].ToArgb() * tmp_2d[x + 1, y + 1].ToArgb() < 0)
            {
                if (Math.Abs(tmp_2d[x - 1, y - 1].ToArgb()) + Math.Abs(tmp_2d[x + 1, y + 1].ToArgb()) > dThreshold)
                {
                    bitmap.SetPixel(x - 1, y - 1, m_ColorForeground);
                }
                else
                {
                    bitmap.SetPixel(x - 1, y - 1, m_ColorBackground);
                }
            }

            else if (tmp_2d[x + 1, y + 1].ToArgb() * tmp_2d[x - 1, y - 1].ToArgb() < 0)
            {
                if (Math.Abs(tmp_2d[x + 1, y + 1].ToArgb()) + Math.Abs(tmp_2d[x - 1, y - 1].ToArgb()) > dThreshold)
                {
                    bitmap.SetPixel(x - 1, y - 1, m_ColorForeground);
                }
                else
                {
                    bitmap.SetPixel(x - 1, y - 1, m_ColorBackground);
                }
            }
            else if (tmp_2d[x, y - 1].ToArgb() * tmp_2d[x, y + 1].ToArgb() < 0)
            {
                if (Math.Abs(tmp_2d[x, y - 1].ToArgb()) + Math.Abs(tmp_2d[x, y + 1].ToArgb()) > dThreshold)
                {
                    bitmap.SetPixel(x - 1, y - 1, m_ColorForeground);
                }
                else
                {
                    bitmap.SetPixel(x - 1, y - 1, m_ColorBackground);
                }
            }
            else
            {
                bitmap.SetPixel(x - 1, y - 1, m_ColorBackground);
            }
        }

        /// <summary>
        /// used to create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new ZeroCrossingEdge(this);
        }
    }
}